import Dep from "./dep.js";
import { arrayMethods } from "./array";

//浏览器是否支持 __proto__
const hasProto = "__proto__" in {};
const arrayKeys = Object.getOwnPropertyNames(arrayMethods);

//工具函数
export function def(obj, key, val, enumerable) {
  Object.defineProperty(obj, key, {
    value: val,
    enumerable: !!enumerable,
    configurable: true,
    writable: true
  });
}
export class Observer {
  constructor(value) {
    this.value = value;
    // this.dep = new Dep();
    def(value, "__ob__", this); //在value上挂载属性为__ob__的Obserber实例
    if (Array.isArray(value)) {
      // const augment = hasProto ? protoAugment : copyAument;
      // augment(value, arrayMethods, arrayKeys);
      this.observeArray(value);
    } else {
      this.walk(value);
    }
  }

  walk(obj) {
    const keys = Object.keys(obj);
    for (let i = 0; i < keys.length; i++) {
      defienReactive(obj, keys[i], obj[keys[i]]);
    }
  }

  /**
   * 检测数组的每一项
   */
  observeArray(items) {
    for (let i = 0, l = items.length; i < l; i++) {
      observer(items[i]);
    }
  }
}

function protoAugment(target, src, keys) {
  target.__proto__ = src; //数组拦截器覆盖到原型上
}

//直接修改数据属性
function copyAument(target, src, keys) {
  for (let i = 0, l = keys.length; i < l; i++) {
    const key = keys[i];
    defienReactive(target, key, src[key]);
  }
}

/**
 * 尝试为value创建一个Obserber实例,
 * 如果创建成功,则直接返回新创建的Obeserver实例
 * 如果value已经存在一个Obserber实例中,则直接返回它
 */
export function observer(value, asRootData) {
  if (!isObject(value)) {
    return;
  }
  let ob;
  if (hasOwn(vale, "__ob__") && value.__ob__ instanceof Observer) {
    ob = value.__ob__;
  } else {
    ob = new Observer(value);
  }
  return ob;
}

function defienReactive(data, key, val) {
  //递归子属性
  let childOb = observer(val);
  let dep = new Dep();
  Object.defineProperty(data, key, {
    configurable: true,
    enumerable: true,
    //取值操作
    get() {
      dep.depend();
      if (childOb) {
        childOb.dep.update();
      }
      return val;
    },
    //赋值操作
    set(newVal) {
      if (val === newVal) {
        return;
      }
      val = newVal;
      dep.notify();
    }
  });
}
